package com.nuc.zyt.day02.Proxy;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @ClassName ProxyTest
 * @Description TODO
 * @Author 耀酱
 * @Date 2023/5/15 14:43
 * @Version 1.0
 */

public class ProxyTest {

        public static void main(String[] args) throws Throwable {
            // 1.得到目标对象
            Person target = new Person();
            // 2.传入目标对象，得到增强对象（如果需要对目标对象进行别的增强，可以另外编写getXxInvocationHandler）
            InvocationHandler logInvocationHandler = getLogInvocationHandler(target);
            // 3.传入接口+增强对象（含目标对象），得到代理对象

//            Eat EatProxy = (Eat) getProxy(
//                    logInvocationHandler,                 // 增强对象（包含 目标对象 + 增强代码）
//                    target.getClass().getClassLoader(),   // 随便传入一个类加载器
//                    target.getClass().getInterfaces()     // 需要代理的接口
//            );

            Eat o =(Eat)Proxy.newProxyInstance(target.getClass().getClassLoader(),
                    target.getClass().getInterfaces(),
                    logInvocationHandler);
            o.eat();
        }
    /**
     * 接口class对象没有构造器，不能new实例
     * 代理proxy的class对象有构造器
     * JDK根据接口生成的其实是Proxy的Class对象，然后根据ProxyClass得到proxy代理对象。proxy代理对象实现了接口，同时也是Proxy类型的。proxy对象的原理是：
     * 内部维护一个InvocationHandler，而InvocationHandler是对增强代码的抽象。通过抽取InvocationHandler，将代理对象和增强代码解耦。
     */

    /**
         * 传入接口+增强（已经包含了目标对象），获取代理对象
         *
         * @param handler
         * @param classLoader
         * @param interfaces
         * @return
         * @throws Exception
         */
        private static Object getProxy(final InvocationHandler handler, final ClassLoader classLoader, final Class<?>... interfaces) throws Exception {
            // 参数1：随便找个类加载器给它 参数2：需要代理的接口
            Class<?> proxyClazz = Proxy.getProxyClass(classLoader, interfaces);
            Constructor<?> constructor = proxyClazz.getConstructor(InvocationHandler.class);
            return constructor.newInstance(handler);
        }

        /**
         * 日志增强代码
         *
         * @param target
         * @return
         */
        private static InvocationHandler getLogInvocationHandler(final Person target) {
            return new InvocationHandler() {
                @Override
                public Object invoke(Object proxy1, Method method, Object[] args) throws Throwable {
                    System.out.println(method.getName() + "方法开始执行...");
                    Object result = method.invoke(target, args);
                    System.out.println(result);
                    System.out.println(method.getName() + "方法执行结束...");
                    return result;
                }
            };
        }
    }

